package org.jing.core.util;

import org.jing.core.lang.JingException;
import org.jing.core.lang.itf.JLoopOne;
import org.jing.core.lang.itf.JLoopTwo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

@SuppressWarnings({ "WeakerAccess", "unused" })
public class GenericUtil {

    /**
     * Description: 不会空指针的情况下统计数组大小. <br>
     *
     * @param array 数组 <br>
     * @param <T> <br>
     * @return <br>
     */
    public static <T> int count(T[] array) {
        return null == array ? 0 : array.length;
    }

    /**
     * Description: 不会空指针的情况下统计列表大小. <br>
     *
     * @param list 数组 <br>
     * @return <br>
     */
    public static int count(List<?> list) {
        return null == list ? 0 : list.size();
    }

    /**
     * Description: 不会空指针的情况下统计Map大小. <br>
     *
     * @param map 数组 <br>
     * @return <br>
     */
    public static int count(Map<?, ?> map) {
        return null == map ? 0 : map.size();
    }

    public static void endLoop() {
        throw new EndLoopException();
    }

    /**
     * Description: for循环 <br>
     *
     * @param array 数组 <br>
     * @param action <br>
     * @param <T> <br>
     */
    public static <T> void loop(T[] array, Consumer<T> action) {
        for (int i$ = 0, l$ = count(array); i$ < l$; i$ ++) {
            try {
                action.accept(array[i$]);
            }
            catch (EndLoopException e) {
                break;
            }
        }
    }

    /**
     * Description: 带下标的for循环 <br>
     *
     * @param array 数组 <br>
     * @param action <br>
     * @param <T> <br>
     */
    public static <T> void loop(T[] array, BiConsumer<Integer, T> action) {
        for (int i$ = 0, l$ = count(array); i$ < l$; i$ ++) {
            try {
                action.accept(i$, array[i$]);
            }
            catch (EndLoopException e) {
                break;
            }
        }
    }

    /**
     * Description: for循环 <br>
     *
     * @param list 集合 <br>
     * @param action <br>
     * @param <T> <br>
     */
    public static <T> void loop(List<T> list, BiConsumer<Integer, T> action) {
        for (int i$ = 0, l$ = count(list); i$ < l$; i$ ++) {
            try {
                action.accept(i$, list.get(i$));
            }
            catch (EndLoopException e) {
                break;
            }
        }
    }

    /**
     * Description: for循环 <br>
     *
     * @param collection 集合 <br>
     * @param action <br>
     * @param <T> <br>
     */
    public static <T> void loop(Collection<T> collection, Consumer<T> action) {
        if (null != collection) {
            try {
                collection.forEach(action);
            }
            catch (EndLoopException ignored) {}
        }
    }

    /**
     * Description: for循环 <br>
     *
     * @param map map <br>
     * @param action <br>
     * @param <K> <br>
     * @param <V> <br>
     */
    public static <K, V> void loop(Map<K, V> map, BiConsumer<K, V> action) {
        if (null != map) {
            try {
                map.forEach(action);
            }
            catch (EndLoopException ignored) {}
        }
    }

    /**
     * Description: for循环 <br>
     *
     * @param object JLoopOne <br>
     * @param action <br>
     * @param <T> <br>
     */
    public static <T> void loop(JLoopOne<T> object, Consumer<T> action) {
        if (null != object) {
            try {
                object.forEach(action);
            }
            catch (EndLoopException ignored) {}
        }
    }

    /**
     * Description: for循环 <br>
     *
     * @param object JLoopTwo <br>
     * @param action <br>
     * @param <A> <br>
     * @param <B> <br>
     */
    public static <A, B> void loop(JLoopTwo<A, B> object, BiConsumer<A, B> action) {
        if (null != object) {
            try {
                object.forEach(action);
            }
            catch (EndLoopException ignored) {}
        }
    }

    /**
     * Description: 获取数组元素而不空指针. <br>
     *
     * @param list 数组 <br>
     * @param index 索引 <br>
     * @param defaultValue 默认值 <br>
     * @param <T> 泛型 <br>
     * @return <br>
     */
    public static <T> T get(List<T> list, int index, T defaultValue) {
        if (count(list) <= index) return defaultValue;
        return list.get(index);
    }

    /**
     * Description: 获取数组元素而不空指针. <br>
     *
     * @param array 数组 <br>
     * @param index 索引 <br>
     * @param defaultValue 默认值 <br>
     * @param <T> 泛型 <br>
     * @return <br>
     */
    public static <T> T get(T[] array, int index, T defaultValue) {
        if (count(array) <= index) return defaultValue;
        return array[index];
    }

    /**
     * Description: 获取Map元素而不空指针. <br>
     *
     * @param map Map <br>
     * @param key 键 <br>
     * @param defaultValue 默认值 <br>
     * @param <K> 键泛型 <br>
     * @param <V> 值泛型 <br>
     * @return <br>
     */
    public static <K, V> V get(Map<K, V> map, K key, V defaultValue) {
        return null == map ? defaultValue : map.getOrDefault(key, defaultValue);
    }

    /**
     * Description: 链式创建List. <br>
     *
     * @param clazz 指定实例类型 <br>
     * @param <T> 泛型-元素 <br>
     * @return 一个ListBuilder, 可以使用<b>add()</b>/<b>addAll()</b>/<b>get()</b>操作<br>
     */
    public static <T> ListBuilder<T> buildList(Class<? extends List> clazz) {
        return ListBuilder.create(clazz);
    }

    /**
     * Description: 链式创建ArrayList. <br>
     *
     * @param <T> 泛型-元素 <br>
     * @return 一个ListBuilder, 可以使用<b>add()</b>/<b>addAll()</b>/<b>get()</b>操作<br>
     */
    public static <T> ListBuilder<T> buildList() {
        return ListBuilder.create(ArrayList.class);
    }

    /**
     * Description: 链式创建Map. <br>
     *
     * @param clazz 指定实例类型 <br>
     * @param <K> 泛型-键 <br>
     * @param <V> 泛型-值 <br>
     * @return 一个MapBuilder, 可以使用<b>put()</b>/<b>putAll()</b>/<b>get()</b>操作<br>
     */
    public static <K, V> MapBuilder<K, V> buildMap(Class<? extends Map> clazz) {
        return MapBuilder.create(clazz);
    }

    /**
     * Description: 链式创建HashMap. <br>
     *
     * @param <K> 泛型-键 <br>
     * @param <V> 泛型-值 <br>
     * @return 一个MapBuilder, 可以使用<b>put()</b>或者<b>get()</b>操作<br>
     */
    public static <K, V> MapBuilder<K, V> buildMap() {
        return MapBuilder.create(HashMap.class);
    }

    /**
     * Description: Sleep. <br>
     *
     * @param time 时间(ms) <br>
     */
    public static void sleep(long time) {
        try {
            Thread.sleep(time);
        }
        catch (Throwable ignored) {}
    }

    public static class ListBuilder<T> {
        private List<T> list;

        private ListBuilder() {}

        static <T> ListBuilder<T> create(Class<? extends List> listClass) {
            try {
                ListBuilder<T> builder = new ListBuilder<>();
                builder.list = (List<T>) listClass.newInstance();
                return builder;
            }
            catch (Throwable t) {
                throw new JingException(t);
            }
        }

        public ListBuilder<T> add(T element) {
            list.add(element);
            return this;
        }

        public ListBuilder<T> add(boolean condition, T element) {
            if (condition) {
                list.add(element);
            }
            return this;
        }

        public ListBuilder<T> addAll(Collection<? extends T> list) {
            this.list.addAll(list);
            return this;
        }

        public ListBuilder<T> addAll(boolean condition, Collection<? extends T> list) {
            if (condition) {
                this.list.addAll(list);
            }
            return this;
        }

        public List<T> get() {
            return list;
        }
    }

    public static class MapBuilder<K, V> {
        private Map<K, V> map;

        private MapBuilder() {}

        static <K, V> MapBuilder<K, V> create(Class<? extends Map> mapClass) {
            try {
                MapBuilder<K, V> builder = new MapBuilder<>();
                builder.map = (Map<K, V>) mapClass.newInstance();
                return builder;
            }
            catch (JingException e) {
                throw e;
            }
            catch (Throwable t) {
                throw new JingException(t);
            }
        }

        public MapBuilder<K, V> put(K key, V value) {
            map.put(key, value);
            return this;
        }

        public MapBuilder<K, V> put(boolean condition, K key, V value) {
            if (condition) {
                map.put(key, value);
            }
            return this;
        }

        public Map<K, V> get() {
            return map;
        }
    }

    private static final class EndLoopException extends RuntimeException {}
}